#include <stdio.h>
#include <CL/cl.h>

int main(void)
{
    // Get Platform
    cl_uint num_platforms;
    printf("----------------------------------\n");
    if (clGetPlatformIDs(0, NULL, &num_platforms)!=CL_SUCCESS){
        printf("Failed to find any OpenCL platforms.\n");
    } else {
        printf("Number of avaliable platforms: %u\n", num_platforms);
    }

    cl_platform_id platform;
    clGetPlatformIDs(1, &platform, NULL);
    printf(" ...First platform chosen\n");

    // Get devices
    cl_uint num_devices;
    if (clGetDeviceIDs(platform, CL_DEVICE_TYPE_ALL, 0,
        NULL, &num_devices)!= CL_SUCCESS){
        printf("Failed to find any OpenCL devices\n");
    } else {
        printf("Number of avaliable devices: %u\n", num_devices);
    }

    cl_device_id devices[num_devices];
    clGetDeviceIDs(platform, CL_DEVICE_TYPE_ALL, num_devices, devices, NULL);
    printf(" ...Devices gathered\n");

    // Platform Queries
    size_t platformProfile_size;
    clGetPlatformInfo(platform, CL_PLATFORM_PROFILE, 0, NULL,
                      &platformProfile_size);

    char platformProfile[platformProfile_size];
    clGetPlatformInfo(platform, CL_PLATFORM_PROFILE,
                      platformProfile_size*sizeof(char),
                      &platformProfile, NULL);
    printf("Platform Profile: %s\n", platformProfile);

    size_t platformVersion_size;
    clGetPlatformInfo(platform, CL_PLATFORM_VERSION, 0, NULL,
                      &platformVersion_size);

    char platformVersion[platformVersion_size];
    clGetPlatformInfo(platform, CL_PLATFORM_VERSION,
                      platformVersion_size*sizeof(char),
                      &platformVersion, NULL);
    printf("Platform Version: %s\n", platformVersion);

    size_t platformName_size;
    clGetPlatformInfo(platform, CL_PLATFORM_NAME, 0, NULL,
                      &platformName_size);

    char platformName[platformName_size];
    clGetPlatformInfo(platform, CL_PLATFORM_NAME,
                      platformName_size*sizeof(char),
                      &platformName, NULL);
    printf("Platform Name: %s\n", platformName);

    size_t platformVendor_size;
    clGetPlatformInfo(platform, CL_PLATFORM_VENDOR, 0, NULL,
                      &platformVendor_size);

    char platformVendor[platformVendor_size];
    clGetPlatformInfo(platform, CL_PLATFORM_VENDOR,
                      platformVendor_size*sizeof(char),
                      &platformVendor, NULL);
    printf("Platform Vendor: %s\n", platformVendor);

    size_t platformExtensions_size;
    clGetPlatformInfo(platform, CL_PLATFORM_EXTENSIONS, 0, NULL,
                      &platformExtensions_size);

    char platformExtensions[platformExtensions_size];
    clGetPlatformInfo(platform, CL_PLATFORM_EXTENSIONS,
                      platformExtensions_size*sizeof(char),
                      &platformExtensions, NULL);
    printf("Platform Extensions: %s\n", platformExtensions);
    printf("----------------------------------\n");

    // Device Queries
    for (int i=0; i<num_devices; i++){

        size_t deviceName_size;
        clGetDeviceInfo(devices[i], CL_DEVICE_NAME, 0, NULL, &deviceName_size);

        char deviceName[deviceName_size];
        clGetDeviceInfo(devices[i], CL_DEVICE_NAME, deviceName_size*sizeof(char),
                        &deviceName, NULL);
        printf("Device Name: %s\n", deviceName);

        cl_device_type deviceType;
        clGetDeviceInfo(devices[i], CL_DEVICE_TYPE, sizeof(cl_device_type),
                        &deviceType, NULL);

        printf("Device Type: ");
        switch(deviceType) {
            case CL_DEVICE_TYPE_CPU:
                printf("CL_DEVICE_TYPE_CPU\n");
                break;
            case CL_DEVICE_TYPE_GPU:
                printf("CL_DEVICE_TYPE_GPU\n");
                break;
            case CL_DEVICE_TYPE_ACCELERATOR:
                printf("CL_DEVICE_TYPE_ACCELERATOR\n");
                break;
            case CL_DEVICE_TYPE_DEFAULT:
                printf("CL_DEVICE_TYPE_DEFAULT\n");
                break;
            default:
                printf("CL_DEVICE_TYPE_ALL\n");
                break;
        }

        cl_uint deviceVendorID;
        clGetDeviceInfo(devices[i], CL_DEVICE_VENDOR_ID, sizeof(cl_uint),
                        &deviceVendorID, NULL);
        printf("Device Vendor ID: %u\n", deviceVendorID);

        cl_uint deviceMCU;
        clGetDeviceInfo(devices[i], CL_DEVICE_MAX_COMPUTE_UNITS, sizeof(cl_uint),
                        &deviceMCU, NULL);
        printf("Device Max Compute Units: %u\n", deviceMCU);

        cl_uint deviceMWID;
        clGetDeviceInfo(devices[i], CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, sizeof(cl_uint),
                        &deviceMWID, NULL);
        printf("Device Max Work Item Dimensions: %u\n", deviceMWID);

        size_t deviceMWIS_size;
        clGetDeviceInfo(devices[i], CL_DEVICE_MAX_WORK_ITEM_SIZES, 0, NULL,
                        &deviceMWIS_size);

        size_t deviceMWIS[deviceMWIS_size];
        clGetDeviceInfo(devices[i], CL_DEVICE_MAX_WORK_ITEM_SIZES,
                        deviceMWIS_size*sizeof(size_t), &deviceMWIS, NULL);

        for (int j=0; j<deviceMWID; j++){
            printf("Device Max Work Item Sizes for %i dimension: %u\n", j+1,
                deviceMWIS[j]);
        }

        size_t deviceMWGS;
        clGetDeviceInfo(devices[i], CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(size_t),
                        &deviceMWGS, NULL);
        printf("Device Max Work Group Size: %u\n", deviceMWGS);

        size_t deviceMCF;
        clGetDeviceInfo(devices[i], CL_DEVICE_MAX_CLOCK_FREQUENCY, sizeof(size_t),
                        &deviceMCF, NULL);
        printf("Device Max Clock Frequency: %u\n", deviceMCF);

        size_t deviceAddressBits;
        clGetDeviceInfo(devices[i], CL_DEVICE_ADDRESS_BITS, sizeof(size_t),
                        &deviceAddressBits, NULL);
        printf("Device Address Bits: %u\n", deviceAddressBits);

        cl_uint deviceMMAS;
        clGetDeviceInfo(devices[i], CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(cl_uint),
                        &deviceMMAS, NULL);
        printf("Device Max Memory Allocation Size: %u\n", deviceMMAS);
        printf("----------------------------------\n");
    }
}
